﻿#define _CRT_SECURE_NO_WARNINGS 1

//指针

//如何规避野指针

//指针初始化
//#include<stdio.h>
//
//int main()
//{
//	int a = 0;
//	int* p1 = &a;
//	int* p2 = NULL;
//
//	return 0;
//}

//⼩⼼指针越界
//#include<stdio.h>
//
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int* p = &arr[0];
//	int size = sizeof(arr) / sizeof(int);
//	int i = 0;
//	for (i = 0; i <= size; i++)
//	{
//		printf("arr[%d] = %d\n", i, *(p + i));//当指针指向的范围超出了数组arr的范围越界访问，指针就会变成野指针
//	}
//
//	return 0;
//}

//指针变量不再使⽤时，及时置NULL，指针使⽤之前检查有效性
//#include<stdio.h>
//
//int main()
//{
//	int arr[5] = { 1,2,3,4,5 };
//	int* p = arr;
//	int size = sizeof(arr) / sizeof(int);
//	int i = 0;
//	for (i = 0; i < size; i++)
//	{
//		printf("arr[%d] = %d\n", i, *p);
//		p++;
//	}
//
//	//此时指针已经越界，应该置空
//	p = NULL;
//	//下次使用时判断是否为空,再使用
//
//	p = &arr[0];//让指针重新获得地址(初始化)
//
//	//使用前判断
//	if (p != NULL)
//	{
//		//...
//	}
//
//	return 0;
//}


//避免返回局部变量的地址
//#include<stdio.h>
//int* test()
//{
//	int a = 7;
//	return &a;
//}
//int main()
//{
//	int* p = test();
//
//	return 0;
//}


//assert 断⾔ --- assert.h 头文件包含了 assert() 运行的时候如果括号的条件符合继续,错误就终止运行
//#include<stdio.h>
//
//#define NDEBUG  //不需要再做断⾔，就在 #include <assert.h> 语句的前⾯，定义⼀个宏 NDEBUG
//#include<assert.h>
//
//int main()
//{
//	int a = 3;
//	//int* p = &a;
//	int* p = NULL;
//	assert(p != NULL);
//
//	return 0;
//}


//指针的使⽤和传址调⽤

//strlen的模拟实现 --- 求字符串⻓度，统计的是字符串中 \0 之前的字符的个数
//#include<stdio.h>
//#include<assert.h>
//size_t my_strlen(const char* str)
//{
//	assert(str != NULL);
//	size_t cnt = 0;
//	while (*str != '\0')
//	{
//		cnt++;
//		str++;
//	}
//	return cnt;
//}
//int main()
//{
//	char str[] = "abcdefgh";
//	size_t len = my_strlen(str);
//	printf("%zd\n", len);
//
//	return 0;
//}


//传值调⽤和传址调⽤
//写⼀个函数，交换两个整型变量的值
//#include<stdio.h>
//////传值调⽤ --- 实参传递给形参的时候，形参会单独创建⼀份临时空间来接收实参，对形参的修改不影响实参,交换失败
////void swap(int x,int y)
////{
////	int tmp = x;
////	x = y;
////	y = tmp;
////}
////传址调⽤ --- 将变量的地址传递给了函数，这种函数调⽤⽅式叫：传址调⽤,交换成功
//void swap(int* x, int* y)
//{
//	int tmp = *x;
//	*x = *y;
//	*y = tmp;
//}
//int main()
//{
//	int a = 3, b = 7;
//	printf("交换前:a = %d,b = %d\n", a, b);
//
//	swap(&a, &b);
//	printf("交换前:a = %d,b = %d\n", a, b);
//
//	return 0;
//}

//数组名的理解

//数组名本来就是地址，⽽且是数组⾸元素的地址
//#include<stdio.h>
//
//int main()
//{
//	int arr[5] = { 1,2,3,4,5 };
//	printf("arr     = %p\n", arr);
//	printf("&arr[0] = %p\n", &arr[0]);
//	//数组名就是数组⾸元素(第⼀个元素)的地址
//	return 0;
//}

//#include<stdio.h>
//int main()
//{
//	int arr[5] = { 1,2,3,4,5 };
//	printf("%d", sizeof(arr));//结果是20   //如果arr是数组⾸元素的地址，那输出应该的应该是4/8才对,这是为什么？
//
//	return 0;
//}

//其实数组名就是数组⾸元素(第⼀个元素)的地址是对的，但是有两个例外：
//sizeof(数组名)，sizeof中单独放数组名，这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
//&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)
//#include<stdio.h>
//
//int main()
//{
//	int arr[5] = { 1,2,3,4,5 };
//	printf("arr     = %p\n", arr);
//	printf("arr+1   = %p\n", arr+1);//跳过1个字节
//
//	printf("&arr[0]   = %p\n", &arr[0]);
//	printf("&arr[0]+1 = %p\n", &arr[0]+1);//跳过1个字节
//
//	printf("&arr    = %p\n", &arr);
//	printf("&arr+1  = %p\n", &arr+1);//跳过20个字节
//
//	return 0;
//}


//使⽤指针访问数组

//#include<stdio.h>
//
//int main()
//{
//	int arr[5] = { 1, 2, 3, 4, 5 };
//	//int* p = &arr[0];
//	int* p = arr;
//	int size = sizeof(arr) / sizeof(int);
//	int i = 0;
//	//输入
//	for (i = 0; i < size; i++)
//	{
//		scanf("%d", p + i);
//		//scanf("%d", arr+i);//也可以这样写
//	}
//	////输出
//	//for (i = 0; i < size; i++)
//	//{
//	//	printf("arr[%d] = %d\n", i, *(p + i));
//	//}
//	//输出
//	for (i = 0; i < size; i++)
//	{
//		printf("arr[%d] = %d\n", i, p[i]);
//	}
//
//	return 0;
//}


//⼀维数组传参的本质

//#include<stdio.h>
////void test(int arr[])//数组传参的本质是传数组首元素的地址
////{
////	int size2 = sizeof(arr) / sizeof(int);
////	printf("size2 = %d\n", size2);
////}
//
//void test(int* p)//⼀维数组传参，形参的部分可以写成数组的形式，也可以写成指针的形式
//{
//	printf("arr[2] = %d\n", p[2]);
//}
//
//int main()
//{
//	int arr[5] = { 1, 2, 3, 4, 5 };
//	int size = sizeof(arr) / sizeof(int);
//	printf("size1 = %d\n", size);
//
//	test(arr);
//
//	return 0;
//}


//冒泡排序
//#include<stdio.h>
//////方法一
////void bubble_sort(int* arr, int size)
////{
////	int i = 0;
////	for (i = 0; i < size; i++)
////	{
////		int j = 0;
////		for (j = 0; j < size - 1 - i; j++)
////		{
////			if (arr[j] > arr[j + 1])
////			{
////				int tmp = arr[j];
////				arr[j] = arr[j + 1];
////				arr[j + 1] = tmp;
////			}
////		}
////	}
////}
////优化 --- 如果其中有一趟比较完都没有出现数字的交换就说明是有序的
//int count = 0;//比较的次数
//int bubble_sort(int* arr, int size)
//{
//	int i = 0;
//	for (i = 0; i < size; i++)
//	{
//		//假设有序
//		int flag = 1;
//		int j = 0;
//		for (j = 0; j < size - 1 - i; j++)
//		{
//			count++;
//			if (arr[j] > arr[j + 1])
//			{
//				//发生交换说明无须
//				flag = 0;
//				int tmp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = tmp;
//			}
//		}
//		if (1 == flag)
//			break;
//	}
//	return count;
//}
//
//int main()
//{
//	//int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
//	int arr[10] = { 0,1,2,3,6,5,7,7,8,9 };
//	int size = sizeof(arr) / sizeof(int);
//	
//	bubble_sort(arr, size);//升序排列
//
//	int i = 0;
//	for (i = 0; i < size; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	printf("\ncount = %d\n",count);
//
//
//	return 0;
//}


//⼆级指针
//#include<stdio.h>
//
//int main()
//{
//	int a = 3;
//	int* pa = &a;//pa是指针变量,指向对象的类型是int,用来存放a的地址
//	int** ppa = &pa;//ppa是指针变量,指向对象的类型是int*,用来存放pa的地址
//	printf("&a = %p\n", &a);
//	printf("pa = %p\n", pa);
//	printf("*pa = %d\n", *pa);
//	printf("&pa = %p\n", &pa);
//	printf("ppa = %p\n", ppa);
//	printf("*ppa = %p\n", *ppa);
//	printf("**ppa = %d\n", **ppa);
//
//	return 0;
//}

//指针数组 --- 用来存放指针的数组,其本质是数组 --- 指针数组的每个元素是地址，⼜可以指向⼀块区域
//#include<stdio.h>
////指针数组模拟⼆维数组
//int main()
//{
//	//int* arr[5];//指针数组
//	int arr1[5] = { 1, 2, 3, 4, 5 };
//	int arr2[5] = { 2, 3, 4, 5, 6 };
//	int arr3[5] = { 3, 4, 5, 6, 7 };
//	
//	int* parr[3] = { arr1, arr2, arr3 };
//
//	int i = 0;
//	for (i = 0; i < 3; i++)
//	{
//		int j = 0;
//		for (j = 0; j < 5; j++)
//		{
//			printf("parr[%d][%d] = %d\n", i, j, parr[i][j]);
//		}
//	}
//
//	return 0;
//}

//字符指针变量
//#include<stdio.h>
//
//int main()
//{
//	//char ch = 'l';
//	//char* pc = &ch;
//	//*pc = 'j';
//	//printf("ch = %c\n", ch);
//
//	const char* ps = "hello c";//本质是把字符串"hello c". ⾸字符的地址放到了ps中
//	//把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 ps 中
//	printf("%s\n", ps);
//
//	return 0;
//}

//#include<stdio.h>
//
//int main()
//{
//	char str1[] = "hello c";
//	char str2[] = "hello c";
//	char* str3 = "hello c";
//	char* str4 = "hello c";
//
//	if (str1 == str2)
//		printf("str1 == str2\n");
//	else
//		printf("str1 != str2\n");// √
//
//	if (str3 == str4)
//		printf("str3 == str4\n");// √
//	else
//		printf("str3 ！= str4\n");
//
//	return 0;
//}

//数组指针 --- 存放的应该是数组的地址，能够指向数组的指针变量
//#include<stdio.h>
//
//int main()
//{
//	//int* p[10];//指针数组
//	int(*p)[10];//数组指针
//	int arr[10] = { 0 };
//	p = &arr;//取出数组的地址
//	printf("p      = %p\n", p);
//	printf("p+1    = %p\n", p+1);
//	printf("arr    = %p\n", arr);
//	printf("arr+1  = %p\n", arr+1);
//	printf("&arr   = %p\n", &arr);
//	printf("&arr+1 = %p\n", &arr+1);
//	//p应该是数组指针,p指向的是数组，数组10个元素，每个元素的类型是int
//
//	return 0;
//}

//#include<stdio.h>
//
//int main()
//{
//	char str[5];
//	char(*p)[5] = &str; //p是数组指针变量
//
//	//char(*)[5] 是数组指针的类型
//
//	return 0;
//}

//#include<stdio.h>
//
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int(*p)[10] = &arr;//将整个数组的地址存在p中
//	int size = sizeof(arr) / sizeof(int);
//	int i = 0;
//	for (i = 0; i < size; i++)
//	{
//		//printf("arr[%d] = %d\n", i, arr[i]);
//		//printf("arr[%d] = %d\n", i, *((*p) + i));
//		printf("arr[%d] = %d\n", i, (*p)[i]);
//								//*&arr[i]
//								//  arr[i]
//	}
//
//	return 0;
//}



//⼆维数组传参的本质
//#include<stdio.h>
//////⼆维数组其实可以看做是每个元素是⼀维数组的数组，也就是⼆维数组的每个元素是⼀个⼀维数组
////void test(int arr[][5], int r, int c)
////{
////	int i = 0, j = 0;
////	for (i = 0; i < r; i++)
////	{
////		for (j = 0; j < c; j++)
////		{
////			printf("%d ", arr[i][j]);
////		}
////		printf("\n");
////	}
////}
//// ⼆维数组传参，形参的部分可以写成数组，也可以写成指针形式
//void test(int (*p)[5], int r, int c)//数组指针
//{
//	int i = 0, j = 0;
//	for (i = 0; i < r; i++)
//	{
//		for (j = 0; j < c; j++)
//		{
//			printf("%d ", *(*(p + i) + j));
//			//printf("%d ", (*(p + i))[j]);
//		}
//		printf("\n");
//	}
//}
//int main()
//{
//	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6, 7} };
//
//	test(arr, 3, 5);//⼆维数组的数组名表⽰的就是第⼀⾏的地址
//	//⼆维数组传参本质上也是传递了地址，传递的是第⼀⾏这个⼀维数组的地址
//
//	return 0;
//}


//函数指针变量
//函数指针变量的写法与数组指针变量的写法非常类似
//#include<stdio.h>
//void test()
//{
//	printf("hello c\n");
//}
//int Add(int x, int y)
//{
//	return x + y;
//}
//int main()
//{
//	printf("test  = %p\n", test);
//	printf("&test = %p\n", &test);
//
//	int arr[5] = { 0 };
//	int(*parr)[5] = &arr;//数组指针
//
//	void (*pf1)() = test;//pf1函数指针变量
//	void (*pf2)() = &test;
//	//void (*)()  是pf1函数指针变量的类型
//	printf("pf1   = %p\n", pf1);
//	printf("pf2   = %p\n", pf2);
//
//	printf("----------------------------------------\n");
//
//	printf("Add   = %p\n", Add);
//	printf("&Add  = %p\n", &Add);
//
//	int (*pf3)(int, int) = Add;//pf3函数指针变量
//	int (*pf4)(int, int) = &Add;
//	//vint (*)(int,int)  是pf3函数指针变量的类型
//	printf("pf3   = %p\n", pf3);
//	printf("pf4   = %p\n", pf4);
//	//函数名 就是函数的地址，当然也可以通过 &函数名 的⽅式获得函数的地址
//	return 0;
//}

//函数指针变量的使⽤
//#include <stdio.h>
//int Add(int x, int y)
//{
//	return x + y;
//}
//int main()
//{
//	int(*pf3)(int, int) = Add;
//
//	printf("%d\n", (*pf3)(2, 3));
//	printf("%d\n", pf3(3, 5));
//	return 0;
//}

//《C陷阱和缺陷》
//int main()
//{
//	(* ( void (*)() ) 0 )();//函数调用
//	// 1. 将0强制类型转换成void (*)()类型的函数指针
//	// 2. 调用0地址处放的这个函数
//
//	return 0;
//}
// 
//void (*signal(int, void(*)(int)))(int)
//函数声明
//声明的函数的名字叫：signal
//signal函数有2个参数，第一个参数的类型是int
//第二个参数的类型是void(*)(int)的函数指针类型，该指针可以指向一个函数，指向的函数参数是int，返回类型是void
//signal函数的返回类型是void(*)(int)的函数指针，该指针可以指向一个函数，指向的函数参数是int，返回类型是void
//


//typedef 关键字 --- ⽤来类型重命名的
#include<stdio.h>

//typedef unsigned int uint;
//
//int main()
//{
//	uint a = 10;//等价于unsigned int a = 10;
//
//	return 0;
//}

////typedef 对指针类型重命名
//typedef int* p_int;
//int main()
//{
//	int*  p1 = NULL;
//	p_int p2 = NULL;
//
//	return 0;
//}

////typedef 对数组指针和函数指针类型重命名有些区别
//int Add(int x, int y)
//{
//	return x + y;
//}
//
//typedef int(*p_arr)[5];
////p_arr 等价于 int(*)[5]
//
//typedef int (*p_f)(int, int);
////p_f 等价于 int (*)(int,int) 
//
//int main()
//{
//	int arr[5];
//	int(*p)[5] = &arr;//p是数组指针变量，p是变量名字
//	//int (*)[5] --- 数组指针类型
//	p_arr p2 = &arr;
//
//	return 0;
//}


//void (*signal(int, void(*)(int)))(int);
////简化后
//typedef void(*ptr_f)(int);
//ptr_f signal(int, ptr_f);




typedef int* ptr_int;
#define PTR_INT int*

int main()
{
	ptr_int p1;//p1是整型指针
	PTR_INT p2;//p2是整型指针

	ptr_int p3, p4;//p3,p4是整型指针
	PTR_INT p5, p6;//p5是整型指针,p6是整型
	//int *p5,p6;

	return 0;
}









